home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / sendmail / sendmail-5.65c+IDA-1.4.4.1 / src / alias.c next >
Encoding:
C/C++ Source or Header  |  1991-08-14  |  17.0 KB  |  768 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted provided
  7.  * that: (1) source distributions retain this entire copyright notice and
  8.  * comment, and (2) distributions including binaries display the following
  9.  * acknowledgement:  ``This product includes software developed by the
  10.  * University of California, Berkeley and its contributors'' in the
  11.  * documentation or other materials provided with the distribution and in
  12.  * all advertising materials mentioning features or use of this software.
  13.  * Neither the name of the University nor the names of its contributors may
  14.  * be used to endorse or promote products derived from this software without
  15.  * specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  17.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  18.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  */
  20.  
  21. #include "sendmail.h"
  22. #include <sys/stat.h>
  23. #include <errno.h>
  24. #ifdef ISC
  25. # include <net/errno.h>
  26. #endif /* ISC */
  27. #include <pwd.h>
  28. #ifdef YP
  29. # include <sys/param.h>
  30. # ifndef MAXHOSTNAMELEN
  31. #  define MAXHOSTNAMELEN    64
  32. # endif /* !MAXHOSTNAMELEN */
  33. #endif /* YP */
  34. #ifndef S_IREAD
  35. # define    S_IREAD        _S_IREAD
  36. #endif /* !S_IREAD */
  37.  
  38. #ifndef lint
  39. # ifdef DBM
  40. static char sccsid[] = "@(#)alias.c    5.21 (Berkeley) 6/1/90 (with DBM)";
  41. static char rcsid[] = "@(#)$Id: alias.c,v 5.21.0.24 1991/08/14 16:56:42 paul Exp $ (with DBM)";
  42. # else /* !DBM */
  43. static char sccsid[] = "@(#)alias.c    5.21 (Berkeley) 6/1/90 (without DBM)";
  44. static char rcsid[] = "@(#)$Id: alias.c,v 5.21.0.24 1991/08/14 16:56:42 paul Exp $ (without DBM)";
  45. # endif /* DBM */
  46. #endif /* not lint */
  47.  
  48. #ifdef __STDC__
  49. static void print_mailer(MAILER *);
  50. static void readaliases(int);
  51. #else /* !__STDC__ */
  52. static void print_mailer();
  53. static void readaliases();
  54. #endif /* __STDC__ */
  55.  
  56. /*
  57. **  ALIAS -- Compute aliases.
  58. **
  59. **    Scans the alias file for an alias for the given address.
  60. **    If found, it arranges to deliver to the alias list instead.
  61. **    Uses libdbm database if -DDBM.
  62. **
  63. **    Parameters:
  64. **        a -- address to alias.
  65. **        sendq -- a pointer to the head of the send queue
  66. **            to put the aliases in.
  67. **
  68. **    Returns:
  69. **        none
  70. **
  71. **    Side Effects:
  72. **        Aliases found are expanded.
  73. **
  74. **    Notes:
  75. **        If NoAlias (the "-n" flag) is set, no aliasing is
  76. **            done.
  77. **
  78. **    Deficiencies:
  79. **        It should complain about names that are aliased to
  80. **            nothing.
  81. */
  82.  
  83.  
  84. #if defined(DBM) && !defined(NDBM) && !defined(OTHERDBM)
  85. # ifdef __STDC__
  86. extern XDATUM fetch(XDATUM);
  87. # else /* !__STDC__ */
  88. extern XDATUM fetch();
  89. # endif /* __STDC__ */
  90. #endif /* DBM && !NDBM && !OTHERDBM */
  91.  
  92. void
  93. alias(a, sendq)
  94.     register ADDRESS *a;
  95.     ADDRESS **sendq;
  96. {
  97.     register char *p;
  98.  
  99.     if (tTd(27, 1))
  100.         printf("alias(%s)\n", a->q_user);
  101.  
  102.     /* don't realias already aliased names */
  103.     if (bitset(QDONTSEND, a->q_flags))
  104.         return;
  105.  
  106.     CurEnv->e_to = a->q_paddr;
  107.  
  108.     /*
  109.     **  Look up this name
  110.     */
  111.  
  112.     if (NoAlias)
  113.         p = NULL;
  114.     else
  115.         p = aliaslookup(a->q_user);
  116.     if (p == NULL)
  117.         return;
  118.  
  119.     /*
  120.     **  Match on Alias.
  121.     **    Deliver to the target list.
  122.     */
  123.  
  124.     if (tTd(27, 1))
  125.         printf("%s (%s, %s) aliased to %s\n",
  126.             a->q_paddr, a->q_host, a->q_user, p);
  127.     message(Arpa_Info, "aliased to %s", p);
  128.  
  129.     /* sendtolist() will detect a possible self-reference for this alias */
  130.     a->q_flags &= ~QSELFREF;
  131.     AliasLevel++;
  132.     sendtolist(p, a, sendq);
  133.     AliasLevel--;
  134.     if (!bitset(QSELFREF, a->q_flags))
  135.         a->q_flags |= QDONTSEND;
  136. }
  137. /*
  138. **  ALIASLOOKUP -- look up a name in the alias file.
  139. **
  140. **    Parameters:
  141. **        name -- the name to look up.
  142. **
  143. **    Returns:
  144. **        the value of name.
  145. **        NULL if unknown.
  146. **
  147. **    Side Effects:
  148. **        none.
  149. **
  150. **    Warnings:
  151. **        The return value will be trashed across calls
  152. **        unless NDBM or OTHERDBM is defined and we're using mapkey().
  153. */
  154.  
  155. char *
  156. aliaslookup(name)
  157.     char *name;
  158. {
  159. #ifdef DBM
  160. # if defined(NDBM) || defined(OTHERDBM)
  161.     char *newname;
  162.  
  163.     if (tTd(27, 3))
  164.         printf("aliaslookup(\"%s\") => ", name);
  165.     newname = mapkey(DB_ALIAS, name, 0, (char *)NULL);
  166.     if (tTd(27, 3))
  167.         printf("%s\n", newname == NULL ? "NOT_FOUND" : newname);
  168.     return newname;
  169.  
  170. # else /* !NDBM && !OTHERDBM */
  171.  
  172.     XDATUM rhs, lhs;
  173.     char *lowname = newstr(name);
  174.  
  175.     /* create a key for fetch */
  176.     (void) makelower(lowname);
  177.     lhs.dsize = strlen(name);
  178.     if (tTd(27, 3))
  179.         printf("aliaslookup(\"%s\") => ", name);
  180.  
  181.     /* first try key as given */
  182.     lhs.dptr = name;
  183.     rhs = fetch(lhs);
  184.     if (rhs.dptr == (char *)NULL)
  185.     {
  186.         /* try null-terminated version */
  187.         lhs.dsize += 1;
  188.         rhs = fetch(lhs);
  189.         lhs.dsize -= 1;
  190.         if (rhs.dptr == (char *)NULL)
  191.         {
  192.             /* try lower-case version */
  193.             lhs.dptr = lowname;
  194.             rhs = fetch(lhs);
  195.             if (rhs.dptr == (char *)NULL)
  196.             {
  197.                 /* try null-terminated lower-case version */
  198.                 lhs.dsize += 1;
  199.                 rhs = fetch(lhs);
  200.             }
  201.         }
  202.     }
  203.     free(lowname);
  204.     if (tTd(27, 3))
  205.         (rhs.dptr) ? printf("%.*s\n", rhs.dsize, rhs.dptr)
  206.                : printf("NOT_FOUND\n");
  207.     return (rhs.dptr);
  208. # endif /* NDBM || OTHERDBM */
  209. #else /* !DBM */
  210.     register STAB *s;
  211.  
  212.     s = stab(name, ST_ALIAS, ST_FIND);
  213.     if (tTd(27, 3))
  214.         printf("%s\n", s == NULL ? "NOT_FOUND" : s->s_alias);
  215.     if (s == NULL)
  216.         return (NULL);
  217.     return (s->s_alias);
  218. #endif /* DBM */
  219. }
  220. /*
  221. **  INITALIASES -- initialize for aliasing
  222. **
  223. **    Very different depending on whether we are running DBM or not.
  224. **
  225. **    Parameters:
  226. **        init -- if set and if DBM, initialize the DBM files.
  227. **
  228. **    Returns:
  229. **        none.
  230. **
  231. **    Side Effects:
  232. **        initializes aliases:
  233. **        if DBM:  opens the database.
  234. **        if ~DBM: reads the aliases into the symbol table.
  235. */
  236.  
  237. #define DBMMODE    0644
  238.  
  239. void
  240. initaliases(init)
  241.     bool init;
  242. {
  243. #ifdef DBM
  244.     int atcnt;
  245.     TIME_TYPE modtime;
  246.     bool automatic = FALSE;
  247.     char buf[MAXNAME];
  248. #endif /* DBM */
  249.     struct stat stb;
  250.     static bool initialized = FALSE;
  251.  
  252.     if (initialized)
  253.         return;
  254.     initialized = TRUE;
  255.  
  256.     if (AliasFile == NULL ||
  257. #ifdef YPMARK
  258.         (AliasFile[0] != YPMARK &&
  259. #endif /* YPMARK */
  260.          stat(AliasFile, &stb) < 0)
  261. #ifdef YPMARK
  262.         )
  263. #endif /* YPMARK */
  264.     {
  265.         if (AliasFile != NULL && init)
  266.             syserr("Cannot open %s", AliasFile);
  267.         NoAlias = TRUE;
  268.         errno = 0;
  269.         return;
  270.     }
  271.  
  272. #ifdef DBM
  273.     /*
  274.     **  Check to see that the alias file is complete.
  275.     **    If not, we will assume that someone died, and it is up
  276.     **    to us to rebuild it.
  277.     */
  278.  
  279. # if !defined(NDBM) && !defined(OTHERDBM)
  280.     if (!init)
  281.         dbminit(AliasFile);
  282. # endif /* !NDBM && !OTHERDBM */
  283.     atcnt = SafeAlias * 2;
  284.     if (atcnt > 0)
  285.         while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
  286.             Xsleep(30);
  287.     else
  288.         atcnt = 1;
  289.  
  290.     /*
  291.     **  See if the DBM version of the file is out of date with
  292.     **  the text version.  If so, go into 'init' mode automatically.
  293.     **    This only happens if our effective userid owns the DBM.
  294.     **    Note the unpalatable hack to see if the stat succeeded.
  295.     */
  296.  
  297.     modtime = stb.st_mtime;
  298.     (void) strcpy(buf, AliasFile);
  299.     (void) strcat(buf, DB_PAGEXT);
  300.     stb.st_ino = 0;
  301.     if (!init &&
  302. # ifdef YPMARK
  303.         AliasFile[0] != YPMARK &&
  304. # endif /* YPMARK */
  305.         (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
  306.     {
  307.         errno = 0;
  308.         if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
  309.         {
  310.             init = TRUE;
  311.             automatic = TRUE;
  312.             message(Arpa_Info, "rebuilding alias database");
  313. # ifdef LOG
  314.             if (LogLevel >= 7)
  315.                 syslog(LOG_INFO, "rebuilding alias database");
  316. # endif /* LOG */
  317.         }
  318.         else
  319.         {
  320. # ifdef LOG
  321.             if (LogLevel >= 7)
  322.                 syslog(LOG_INFO, "alias database out of date");
  323. # endif /* LOG */
  324.             message(Arpa_Info, "Warning: alias database out of date");
  325.         }
  326.     }
  327.  
  328.     /*
  329.     **  If necessary, load the DBM file.
  330.     **    If running without DBM, load the symbol table.
  331.     */
  332.  
  333.     if (init)
  334.     {
  335. # ifdef LOG
  336.         if (LogLevel >= 6)
  337.             syslog(LOG_NOTICE, "alias database %srebuilt by %s",
  338.                 automatic ? "auto" : "", username());
  339. # endif /* LOG */
  340.         readaliases(TRUE);
  341.     }
  342. #else /* !DBM */
  343.     readaliases(init);
  344. #endif /* DBM */
  345. }
  346. /*
  347. **  READALIASES -- read and process the alias file.
  348. **
  349. **    This routine implements the part of initaliases that occurs
  350. **    when we are not going to use the DBM stuff.
  351. **
  352. **    Parameters:
  353. **        init -- if set, initialize the DBM stuff.
  354. **
  355. **    Returns:
  356. **        none.
  357. **
  358. **    Side Effects:
  359. **        Reads AliasFile into the symbol table.
  360. **        Optionally, builds the .dir & .pag files.
  361. */
  362.  
  363. static void
  364. readaliases(init)
  365.     bool init;
  366. {
  367.     register char *p;
  368.     char *rhs;
  369.     bool skipping;
  370.     int naliases, bytes, longest;
  371.     FILE *af;
  372.     SIG_TYPE (*oldsigint)();
  373.     ADDRESS al, bl;
  374.     register STAB *s;
  375.     char line[BUFSIZ];
  376.     char longest_lhs[BUFSIZ];
  377.  
  378. #ifdef YPMARK
  379.     if (AliasFile[0] == YPMARK)
  380.     {
  381.         if (tTd(27, 1))
  382.             printf("Can't reinit YP databases: \"%s\"\n", AliasFile);
  383.         /* reuse old aliases */
  384.         errno = 0;
  385.         return;
  386.     }
  387. #endif /* YPMARK */
  388.     /*
  389.      * We can't get an exclusive lock on a file that isn't opened for
  390.      * writing on most systems - sigh!
  391.      */
  392.     if ((af = fopen(AliasFile, "r+")) == NULL)
  393.     {
  394.         if (tTd(27, 1))
  395.             printf("Can't open %s\n", AliasFile);
  396.         errno = 0;
  397.         NoAlias++;
  398.         return;
  399.     }
  400.  
  401. #ifdef DBM
  402.     /* see if someone else is rebuilding the alias file already */
  403.     if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 &&
  404.         (errno == EWOULDBLOCK || errno == EAGAIN))
  405.     {
  406.         /* yes, they are -- wait until done and then return */
  407.         message(Arpa_Info, "Alias file is already being rebuilt");
  408.         if (OpMode != MD_INITALIAS)
  409.         {
  410.             /* wait for other rebuild to complete */
  411.             (void) flock(fileno(af), LOCK_EX);
  412.         }
  413.         (void) fclose(af);
  414.         errno = 0;
  415.         return;
  416.     }
  417. #endif /* DBM */
  418.  
  419.     /*
  420.     **  If initializing, create the new DBM files.
  421.     */
  422.  
  423.     if (init)
  424.     {
  425.         oldsigint = signal(SIGINT, SIG_IGN);
  426.         (void) strcpy(line, AliasFile);
  427.         (void) strcat(line, DB_PAGEXT);
  428.         if (close(creat(line, DBMMODE)) < 0)
  429.         {
  430.             syserr("cannot make %s", line);
  431.             (void) signal(SIGINT, oldsigint);
  432.             return;
  433.         }
  434.         (void) strcpy(line, AliasFile);
  435.         (void) strcat(line, DB_DIREXT);
  436.         if (close(creat(line, DBMMODE)) < 0)
  437.         {
  438.             syserr("cannot make %s", line);
  439.             (void) signal(SIGINT, oldsigint);
  440.             return;
  441.         }
  442. #if defined(NDBM) || defined(OTHERDBM)
  443.         (void) mapinit(DB_ALIAS);
  444. #else /* !NDBM && !OTHERDBM */
  445.         dbminit(AliasFile);
  446. #endif /* NDBM || OTHERDBM */
  447.     }
  448.  
  449.     /*
  450.     **  Read and interpret lines
  451.     */
  452.  
  453.     FileName = AliasFile;
  454.     LineNumber = 0;
  455.     naliases = bytes = longest = 0;
  456.     *longest_lhs = '\0';
  457.     skipping = FALSE;
  458.     while (fgets(line, sizeof (line), af) != NULL)
  459.     {
  460.         int lhssize, rhssize;
  461.  
  462.         LineNumber++;
  463.         p = index(line, '\n');
  464.         if (p != NULL)
  465.             *p = '\0';
  466.         switch (line[0])
  467.         {
  468.           case '#':
  469.           case '\0':
  470.             skipping = FALSE;
  471.             continue;
  472.  
  473.           case ' ':
  474.           case '\t':
  475.             if (!skipping)
  476.                 syserr("Non-continuation line starts with space");
  477.             skipping = TRUE;
  478.             continue;
  479.         }
  480.         skipping = FALSE;
  481.  
  482.         /*
  483.         **  Process the LHS
  484.         **    Find the first colon, and parse the address.
  485.         **    It should resolve to a local name -- this will
  486.         **    be checked later (we want to optionally do
  487.         **    parsing of the RHS first to maximize error
  488.         **    detection).
  489.         */
  490.  
  491.         for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
  492.             continue;
  493.         if (*p++ != ':')
  494.         {
  495.             syserr("missing colon");
  496.             continue;
  497.         }
  498.         if (parseaddr(line, &al, 1, ':') == NULL)
  499.         {
  500.             syserr("illegal alias name");
  501.             continue;
  502.         }
  503.         loweraddr(&al);
  504.  
  505.         /*
  506.         **  Process the RHS.
  507.         **    'al' is the internal form of the LHS address.
  508.         **    'p' points to the text of the RHS.
  509.         */
  510.  
  511.         rhs = p;
  512.         for (;;)
  513.         {
  514.             register char c;
  515.  
  516.             if (init && CheckAliases)
  517.             {
  518.                 /* do parsing & compression of addresses */
  519.                 while (*p != '\0')
  520.                 {
  521.                     extern char *DelimChar;
  522.  
  523.                     while (isspace(*p) || *p == ',')
  524.                         p++;
  525.                     if (*p == '\0')
  526.                         break;
  527.                     if (parseaddr(p, &bl, -1, ',') == NULL)
  528.                         usrerr("%s... bad address", p);
  529.                     p = DelimChar;
  530.                 }
  531.             }
  532.             else
  533.             {
  534.                 p = &p[strlen(p)];
  535.                 if (p[-1] == '\n')
  536.                     *--p = '\0';
  537.             }
  538.  
  539.             /* see if there should be a continuation line */
  540.             c = fgetc(af);
  541.             if (!feof(af))
  542.                 (void) ungetc(c, af);
  543.             if (c != ' ' && c != '\t')
  544.                 break;
  545.  
  546.             /* read continuation line */
  547.             if (fgets(p, sizeof line - (p - line), af) == NULL)
  548.                 break;
  549.             LineNumber++;
  550.         }
  551.         if (al.q_mailer != LocalMailer)
  552.         {
  553.             syserr("cannot alias non-local names");
  554.             {
  555.                 printf("Mailer al.q_mailer:\n");
  556.                 print_mailer(al.q_mailer);
  557.                 printf("\nMailer LocalMailer:\n");
  558.                 print_mailer(LocalMailer);
  559.             }
  560.             continue;
  561.         }
  562.  
  563.         /*
  564.         **  Insert alias into symbol table or DBM file
  565.         */
  566.  
  567.         lhssize = strlen(al.q_user);
  568.         rhssize = strlen(rhs);
  569. #ifndef NO_PADDING
  570.         lhssize++;
  571.         rhssize++;
  572. #endif /* !NO_PADDING */
  573.  
  574. #ifdef DBM
  575.         if (init)
  576.         {
  577.             XDATUM key, content;
  578.  
  579.             key.dsize = lhssize;
  580.             key.dptr = al.q_user;
  581.             content.dsize = rhssize;
  582.             content.dptr = rhs;
  583.             if (
  584. # if defined(NDBM) || defined(OTHERDBM)
  585.                 dbm_store(AliasDbm, key, content, DBM_REPLACE)
  586. # else /* !NDBM && !OTHERDBM */
  587.                 store(key, content)
  588. # endif /* NDBM || OTHERDBM */
  589.                 < 0)
  590.                 syserr("DBM store of %s (size %d) failed", al.q_user, (lhssize+rhssize));
  591.         }
  592.         else
  593. #endif /* DBM */
  594.         {
  595.             s = stab(al.q_user, ST_ALIAS, ST_ENTER);
  596.             s->s_alias = newstr(rhs);
  597.         }
  598.  
  599.         /* statistics */
  600.         naliases++;
  601.         bytes += lhssize + rhssize;
  602.         if ((rhssize + lhssize) > longest)
  603.         {
  604.             longest = rhssize + lhssize;
  605.             (void) strcpy(longest_lhs, al.q_user);
  606.         }
  607.     }
  608.  
  609. #ifdef DBM
  610.     if (init)
  611.     {
  612.         XDATUM key;
  613. # ifdef YP
  614.         XDATUM content;
  615.         char    Now[MAXHOSTNAMELEN+1];
  616.  
  617.         /* add the YP stamps.  N.B., don't pad the lengths by 1! */
  618.         gethostname (Now, MAXHOSTNAMELEN);
  619.         key.dsize = strlen ("YP_MASTER_NAME");
  620.         key.dptr = "YP_MASTER_NAME";
  621.         content.dsize = strlen (Now);
  622.         content.dptr = Now;
  623. #  if defined(NDBM) || defined(OTHERDBM)
  624.         (void) dbm_store(AliasDbm, key, content, DBM_INSERT);
  625. #  else /* !NDBM && !OTHERDBM */
  626.         store(key, content);
  627. #  endif /* NDBM || OTHERDBM */
  628.         (void) sprintf (Now, "%010u", time(0));
  629.         key.dsize = strlen ("YP_LAST_MODIFIED");
  630.         key.dptr = "YP_LAST_MODIFIED";
  631.         content.dsize = strlen (Now);
  632.         content.dptr = Now;
  633. #  if defined(NDBM) || defined(OTHERDBM)
  634.         (void) dbm_store(AliasDbm, key, content, DBM_INSERT);
  635. #  else /* !NDBM && !OTHERDBM */
  636.         store(key, Now);
  637. #  endif /* NDBM || OTHERDBM */
  638. # endif /* YP */
  639.  
  640.         /* add the distinquished alias "@" */
  641.         key.dsize = 2;
  642.         key.dptr = "@";
  643. # if defined(NDBM) || defined(OTHERDBM)
  644.         (void) dbm_store(AliasDbm, key, key, DBM_INSERT);
  645. # else /* !NDBM && !OTHERDBM */
  646.         store(key, key);
  647. # endif /* NDBM || OTHERDBM */
  648.  
  649.         /* restore the old signal */
  650.         (void) signal(SIGINT, oldsigint);
  651.     }
  652. #endif /* DBM */
  653.  
  654.     /* closing the alias file drops the lock */
  655.     (void) fclose(af);
  656.     CurEnv->e_to = NULL;
  657.     FileName = NULL;
  658.     message(Arpa_Info, "%d aliases, longest (%s) %d bytes, %d bytes total",
  659.             naliases, longest_lhs, longest, bytes);
  660. #ifdef LOG
  661.     if (LogLevel >= 8)
  662.         syslog(LOG_INFO, "%d aliases, longest (%s) %d bytes, %d bytes total",
  663.             naliases, longest_lhs, longest, bytes);
  664. #endif /* LOG */
  665. }
  666. /*
  667. **  FORWARD -- Try to forward mail
  668. **
  669. **    This is similar but not identical to aliasing.
  670. **
  671. **    Parameters:
  672. **        user -- the name of the user who's mail we would like
  673. **            to forward to.  It must have been verified --
  674. **            i.e., the q_home field must have been filled
  675. **            in.
  676. **        sendq -- a pointer to the head of the send queue to
  677. **            put this user's aliases in.
  678. **
  679. **    Returns:
  680. **        none.
  681. **
  682. **    Side Effects:
  683. **        New names are added to send queues.
  684. */
  685.  
  686. void
  687. forward(user, sendq)
  688.     ADDRESS *user;
  689.     ADDRESS **sendq;
  690. {
  691.     char buf[60];
  692.  
  693.     if (tTd(27, 1))
  694.         printf("forward(%s)\n", user->q_paddr);
  695.  
  696.     if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
  697.         return;
  698.     if (user->q_home == NULL)
  699.         syserr("forward: no home");
  700.  
  701.     /* good address -- look for .forward file in home */
  702.     define('z', user->q_home, CurEnv);
  703.     expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
  704.     if (!safefile(buf, user->q_uid, S_IREAD))
  705.         return;
  706.  
  707.     /*
  708.      * we do have an address to forward to -- do it but
  709.      * don't carry over selfref from alias.
  710.      */
  711.     user->q_flags &= ~QSELFREF;
  712.     include(buf, "forwarding", user, sendq);
  713. }
  714. /*
  715. **  PRINT_MAILER -- Print contents of struct mailer
  716. **
  717. **    This is for debugging
  718. **
  719. **    Parameters:
  720. **        Mpnt -- pointer to struct mailer
  721. **
  722. **    Returns:
  723. **        none.
  724. **
  725. **    Side Effects:
  726. **        none.
  727. */
  728.  
  729. static void
  730. print_mailer(Mpnt)
  731.     MAILER    *Mpnt;
  732. {
  733.     register int    i;
  734.     register char    **j;
  735.  
  736.     if (Mpnt == (MAILER *) NULL)
  737.     {
  738.         printf("Null MAILER pointer\n");
  739.         return;
  740.     }
  741.     printf("m_name (symbolic name) %s\n",
  742.         (Mpnt->m_name == (char *)NULL) ? "NULL" : Mpnt->m_name);
  743.     printf("m_mailer (pathname) %s\n",
  744.         (Mpnt->m_mailer == (char *)NULL) ? "NULL" : Mpnt->m_mailer);
  745.     printf("m_flags BITMAP:    ");
  746.     for (i = 0; i < (BITMAPBYTES / sizeof (int)); i++)
  747.         printf("  %X", Mpnt->m_flags[i]);
  748.     printf("\n");
  749.     printf("m_mno (internal mailer number) %d\n", (int) Mpnt->m_mno);
  750.     printf("m_argv (template argument vector):  ");
  751.     for (j = Mpnt->m_argv; *j != (char *) NULL; j++)
  752.         printf(" \"%s\"", *j);
  753.     printf("\n");
  754.     printf("m_se_rwset (rewriting ruleset for envelope senders): %d\n",
  755.         (int) Mpnt->m_se_rwset);
  756.     printf("m_sh_rwset (rewriting ruleset for header senders): %d\n",
  757.         (int) Mpnt->m_sh_rwset);
  758.     printf("m_re_rwset (rewriting ruleset for envelope recipients): %d\n",
  759.         (int) Mpnt->m_re_rwset);
  760.     printf("m_rh_rwset (rewriting ruleset for header recipient): %d\n",
  761.         (int) Mpnt->m_rh_rwset);
  762.     printf("m_eol (end of line string) %s\n",
  763.         (Mpnt->m_eol == (char *)NULL) ? "NULL" : Mpnt->m_eol);
  764.     printf("m_maxsize (size limit on message to this mailer): %ld\n",
  765.         Mpnt->m_maxsize);
  766.     return;
  767. }
  768.